目錄
以下範例是使用 Python 自建一個 http server,第一次使用 Chrome 擔任 HTTP client,並取得回應;接著利用 telnet 程式擔任 HTTP client,telnet會完成 TCP/IP 協定組的底層傳輸層協定(tcp),接著我們手動輸入 http request。如下圖所示,右手邊是使用 flask 所撰寫的 http server,而左手邊則是分別使用 chrome 與 telnet 所扮演的 http client。
圖 1. 使用flask與telnet來完成 HTTP 請求與回應
以下範例是使用flask框架自建的 web server。
from flask import Flask, request
app = Flask(__name__)
@app.route('/', methods=['GET'])
def home():
return "<a href='/login'>Go to login page</a>"
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
return 'Hello ' + request.values['username']
return "請輸入姓名<form method='post' action='/login'><input type='text' name='username' />" \
"</br>" \
"<button type='submit'>Submit</button></form>"
if __name__ == '__main__':
app.run()
執行結果如下圖所示
圖 2. 使用 flask 建立的 Web Server
會建立一個 Web Server,網址為 http://127.0.0.1:5000/
圖 3. 使用 flask 建立的 Web Server
有建立兩個請求回應,一個是 GET,一個是 POST,對應的路徑都是 /login
請輸入姓名<form method='post' action='/login'><input type='text' name='username' /></br><button type='submit'>Submit</button></form>
主體內容。Hello yehchitsai
主體內容。使用 Chrome 觀察 http request的實際內容,打開 Chrome DevTools 畫面,抓取 get 與 post 的請求內容。
GET 請求內容
GET /login HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6
Cache-Control: no-cache
Connection: keep-alive
Host: 127.0.0.1:5000
Pragma: no-cache
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36
sec-ch-ua: "Not)A;Brand";v="99", "Google Chrome";v="127", "Chromium";v="127"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"
圖 4. 使用 chrome 的 GET 請求內容
POST 請求內容
POST /login HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 19
Content-Type: application/x-www-form-urlencoded
Host: 127.0.0.1:5000
Origin: http://127.0.0.1:5000
Pragma: no-cache
Referer: http://127.0.0.1:5000/login
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36
sec-ch-ua: "Not)A;Brand";v="99", "Google Chrome";v="127", "Chromium";v="127"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"
username=yehchitsai
圖 5. 使用 chrome 的 POST 請求內容
可以很清楚的看出來,相同的網址,卻因不同的請求方法(get/post)而有不同的回應,而這原因來自於後端的 flask 判斷。
使用 telnet 工具程式手動輸入 http request的內容,將可以得到 chrome 一樣的結果,只是因為 telnet 並不會把所得到的結果以圖形化的方式展示。
在 windows 上啟用 Telnet,可以參考這篇文章,在終端機上使用telnet連到 flask web server,並輸入 get 請求。
終端機的指令如下:
telnet 127.0.0.1 5000
手動輸入 GET 請求內容
接著貼上上一節的 chrome 所取得的GET 請求內容,並按下兩次Enter、兩次Enter、兩次Enter(很重要所以說三遍),這就是 http 請求格式所提到的 <CR><LF>
,將標頭跟本體訊息區隔,輸入兩次 Enter 的原因是因為 get 沒有本體訊息,所以只好直接輸入 Enter。這樣一來就可以得到http回應標頭與主體訊息。
圖 6. 使用 telnet 輸入 http GET request 的內容
其實可以不用輸入那麼多的請求標頭也是可以得到回應的,比方說只輸入host標頭。
GET /login HTTP/1.1
Host: 127.0.0.1:5000
圖 7. 使用 telnet 輸入 http GET request 的內容
手動輸入 POST 請求內容
接著貼上上一節的 Chrome 所取得的POST 請求內容,並按下一次Enter,這樣就可以得到 HTTP 回應標頭與主體訊息。
圖 8. 使用 telnet 輸入 http POST request 的內容